﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
using System.Text;

namespace BfLib.Text.Html
{
    public struct TagParts
    {
        public string Tag;
        public string AttributeValue;
        public string InnerText;

        public override string ToString()
        {
            return this.AttributeValue;
        }
    }

    public class TagExtracter
    {
   
        private List<TagParts> _tagPartsList;

        private string _attribute = string.Empty;
        private string _html=string.Empty;
        private string _tagType = string.Empty;
        private bool _is2PartTag = false;
        
        private const string ArgExHtml = "HTML argument cannot be zero length";
        private const string ArgExTag = "Tag argument cannot be zero length";
        private const string ArgExAttribute = "Attribute argument cannot be zero length";
        private const string ArgExHtmlNull = "HTML argument cannot be null";
        private const string ArgExTagNull = "Tag argument cannot be null";
        private const string ArgExAttributeNull = "Attribute argument cannot be null";

        public TagExtracter()
        {
            _tagPartsList = new List<TagParts>();
            StripCommentsAndScripts();
        }

        public TagExtracter(string Html)
        {
            this._html = Html;
            _tagPartsList = new List<TagParts>();

            if (this._html == null) throw new ArgumentNullException(ArgExHtmlNull);
            else if (this._html.Length == 0) throw new ArgumentException(ArgExHtml);

            StripCommentsAndScripts();
        }

        public TagExtracter(string Html, string TagType, bool Is2PartTag)
        {
            this._tagPartsList = new List<TagParts>();
            this._html = Html;
            this._tagType = TagType;
            this._is2PartTag = Is2PartTag;

            if (this._html == null) throw new ArgumentNullException(ArgExHtmlNull);
            else if (TagType == null) throw new ArgumentNullException(ArgExTagNull);
            else if (this._html.Length == 0) throw new ArgumentException(ArgExHtml);
            else if (TagType.Length == 0) throw new ArgumentException(ArgExTag);

            StripCommentsAndScripts();

            //LoadTags();
            FindTags();
        }

        private void StripCommentsAndScripts()
        {
            this._html = this._html.RemoveHTMLComments();
            this._html = this._html.RemoveHTMLScripts();
        }

        public TagExtracter(string Html, string TagType, bool Is2PartTag, string Attribute)
        {
            this._tagPartsList = new List<TagParts>();
            
            this._html = Html;
            this._attribute = Attribute;
            this._tagType = TagType;
            this._is2PartTag = Is2PartTag;

            if (this._html == null) throw new ArgumentNullException(ArgExHtmlNull);
            else if (TagType == null) throw new ArgumentNullException(ArgExTagNull);
            else if (Attribute == null) throw new ArgumentNullException(ArgExAttributeNull);
            else if (this._html.Length == 0) throw new ArgumentException(ArgExHtml);
            else if (TagType.Length == 0) throw new ArgumentException(ArgExTag);
            else if (Attribute.Length == 0) throw new ArgumentException(ArgExAttribute);

            StripCommentsAndScripts();
            //LoadTags();
            FindTags();
        }

        private void LoadTags()
        {
            this._tagPartsList.Clear();
            string tagPattern = string.Empty;
            Regex regexObj;
            Match matchResult;
            TagParts tp;

            if (_is2PartTag)
            {
                tagPattern = @"<\s*XXTAGHEREXX(.|\n)*?>(.|\n)*?<\s*/\s*XXTAGHEREXX\s*>".Replace("XXTAGHEREXX", this._tagType);
                try
                {
                    regexObj = new Regex(tagPattern, RegexOptions.Singleline);
                    matchResult = regexObj.Match(_html);
                    while (matchResult.Success)
                    {
                        tp = new TagParts();
                        tp.Tag = matchResult.Value;

                        //if (_attribute.Length > 0)
                        //{
                        tp.AttributeValue = tp.Tag.GetAttibute( _attribute).Replace("\\\"", string.Empty);
                        tp.InnerText = tp.Tag.StripTagsCharArray();
                        //}

                        _tagPartsList.Add(tp);
                        matchResult = matchResult.NextMatch();
                    }

                }
                catch// (ArgumentException ex)
                {
                    throw;
                }
            }
            else
            {
                tagPattern = @"<\s*XXTAGHEREXX(.|\n)*?>".Replace("XXTAGHEREXX", this._tagType);
                try
                {
                    regexObj = new Regex(tagPattern, RegexOptions.Singleline);
                    matchResult = regexObj.Match(_html);
                    while (matchResult.Success)
                    {
                        tp = new TagParts();
                        tp.Tag = matchResult.Value;

                        //if (_attribute.Length > 0)
                        //{
                        tp.AttributeValue = tp.Tag.GetAttibute( _attribute).Replace("\\\"", string.Empty);
                        tp.InnerText = string.Empty;
                        //}

                        _tagPartsList.Add(tp);
                        matchResult = matchResult.NextMatch();
                    }
                }
                catch// (ArgumentException ex)
                {
                    throw;
                }
            }
        }









        public void FindTags()
        {
            string regex;// = String.Format(@"(<{0}.*?>.*?</{1}>)", this._tagType, this._tagType);
            MatchCollection m1;// = Regex.Matches(_html, regex, RegexOptions.Singleline);
            TagParts i;
            string value = string.Empty;

            if (this._is2PartTag)
            {
                //Match m2;
                regex = String.Format(@"(<{0}.*?>.*?</{1}>)", this._tagType, this._tagType);
                m1 = Regex.Matches(_html, regex, RegexOptions.Singleline);

                foreach (Match m in m1)
                {
                    value = m.Groups[1].Value;
                    i = new TagParts();
                    i.Tag = value;

                    //Match m2 = Regex.Match(value, @"href=\""(.*?)\""", RegexOptions.Singleline);
                    //m2 = Regex.Match(value, String.Format(@"{0}\s=\s\""(.*?)\""", this._attribute), RegexOptions.Singleline);
                    //if (m2.Success)
                    //{
                    //    i.AttributeValue = m2.Groups[1].Value;
                    //}

                    i.AttributeValue = value.GetAttibute( this._attribute);

                    // 4.
                    // Remove inner tags from text.
                    i.InnerText = Regex.Replace(value, @"\s*<.*?>\s*", "", RegexOptions.Singleline);

                    _tagPartsList.Add(i);
                }
            }
            else
            {
                //Match m2;
                regex = String.Format(@"(<{0}.*?>)", this._tagType);
                m1 = Regex.Matches(_html, regex, RegexOptions.Singleline);

                foreach (Match m in m1)
                {
                    value = m.Groups[1].Value;
                    i = new TagParts();
                    i.Tag = value;
                    i.AttributeValue = value.GetAttibute(this._attribute);

                    // 4.
                    // Remove inner tags from text.
                    i.InnerText = string.Empty;

                    _tagPartsList.Add(i);
                }

            }
        }

        public void SetHtml(string Html)
        {
            this._html = Html;

            if (Html == null) throw new ArgumentException(ArgExHtmlNull);
            else if (Html.Length == 0) throw new ArgumentException(ArgExHtml);
        }

        public void ExtractTags(string Html, string TagType, bool Is2PartTag)
        {
            this._html = Html;
            this._is2PartTag = Is2PartTag;
            this._tagType = TagType;

            if (Html == null) throw new ArgumentNullException(ArgExHtmlNull);
            else if (TagType == null) throw new ArgumentNullException(ArgExTagNull);
            else if (Html.Length == 0) throw new ArgumentException(ArgExHtml);
            else if (TagType.Length == 0) throw new ArgumentException(ArgExTag);

            //LoadTags();
            FindTags();
        }

        public void ExtractTagAttributeValues(string Html, string TagType, string Attribute, bool Is2PartTag)
        {
            this._html = Html;
            this._tagType = TagType;
            this._is2PartTag = Is2PartTag;
            this._attribute = Attribute;

            if (Html == null) throw new ArgumentNullException(ArgExHtmlNull);
            else if (TagType == null) throw new ArgumentNullException(ArgExTagNull);
            else if (Attribute == null) throw new ArgumentNullException(ArgExAttributeNull);
            else if (Html.Length == 0) throw new ArgumentException(ArgExHtml);
            else if (TagType.Length == 0) throw new ArgumentException(ArgExTag);
            else if (Attribute.Length == 0) throw new ArgumentException(ArgExAttribute);
            
            FindTags();
            //LoadTags();
        }
















        public string TagType
        {
            get
            {
                return _tagType;
            }
        }

        public string TagAttribute
        {
            get
            {
                return _attribute;
            }
        }

        public List<TagParts> Tags
        {
            get
            {
                return _tagPartsList;
            }
        }
    }
}
